package com.lantosec.hmrp.core.log;

import com.alibaba.fastjson.JSON;
import com.lantosec.hmrp.core.shiro.TokenUtil;
import com.lantosec.hmrp.core.util.UUIDUtils;
import com.lantosec.hmrp.modules.system.entity.SysLog;
import com.lantosec.hmrp.modules.system.service.ISysLogService;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 杨成宇
 * @version 1.0
 */
@Aspect
@Component
@Order(100)
@Log4j2
public class LogAspect {

    @Autowired
    ISysLogService sysLogService;

    private ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>();

    /**
     * 横切点
     */
    @Pointcut("execution(public * com.lantosec.hmrp.modules.*.controller..*.*(..))")
    public void webLog() {
    }

    /**
     * 接收请求，并记录数据
     * @param joinPoint
     * @param controllerWebLog
     */
    @Before(value = "webLog()&& @annotation(controllerWebLog)")
    public void doBefore(JoinPoint joinPoint, ControllerLog controllerWebLog) {
        // 接收到请求
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        // 记录请求内容，threadInfo存储所有内容
        Map<String, Object> threadInfo = new HashMap<>();
        log.info("URL : " + request.getRequestURL());
        threadInfo.put("url", request.getRequestURL());
        log.info("URI : " + request.getRequestURI());
        threadInfo.put("uri", request.getRequestURI());
        log.info("HTTP_METHOD : " + request.getMethod());
        threadInfo.put("httpMethod", request.getMethod());
        log.info("REMOTE_ADDR : " + request.getRemoteAddr());
        threadInfo.put("ip", request.getRemoteAddr());
        log.info("parameter：" + request.getParameterMap());
        threadInfo.put("parameter", request.getParameterMap());
        log.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
                + joinPoint.getSignature().getName());
        threadInfo.put("classMethod",
                joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
        threadInfo.put("args", Arrays.toString(joinPoint.getArgs()));
        log.info("USER_AGENT"+request.getHeader("User-Agent"));
        threadInfo.put("userAgent", request.getHeader("User-Agent"));
        log.info("执行方法：" + controllerWebLog.name());
        threadInfo.put("methodName", controllerWebLog.name());
        threadInfo.put("methodType", controllerWebLog.type());
        threadLocal.set(threadInfo);
    }
    /**
     * 执行成功后处理
     * @param controllerWebLog
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(value = "webLog()&& @annotation(controllerWebLog)", returning = "ret")
    public void doAfterReturning(ControllerLog controllerWebLog, Object ret) throws Throwable {
        Map<String, Object> threadInfo = threadLocal.get();
        threadInfo.put("result", ret);
        if (controllerWebLog.intoDb()) {
            // 数据库操作
            insertResult(threadInfo);
        }
        // 处理完请求，返回内容
        log.info("RESPONSE : " + ret);
    }

    private void insertResult(Map<String, Object> threadInfo) {
        try {
            SysLog sysLog = new SysLog();
            sysLog.setClassMethod(String.valueOf(threadInfo.get("classMethod")));
            sysLog.setUrl(String.valueOf(threadInfo.get("url")));
            sysLog.setUri(String.valueOf(threadInfo.get("uri")));
            sysLog.setHttpMethod(String.valueOf(threadInfo.get("httpMethod")));
            sysLog.setIp(String.valueOf(threadInfo.get("ip")));
            sysLog.setParameter(JSON.toJSONString(threadInfo.get("parameter")));
            sysLog.setResult(JSON.toJSONString(threadInfo.get("result")));
            sysLog.setUserAgent(String.valueOf(threadInfo.get("userAgent")));
            sysLog.setLogName(String.valueOf(threadInfo.get("methodName")));
            sysLog.setLogType(String.valueOf(threadInfo.get("methodType")));
            sysLog.setUserId(TokenUtil.getUserId());
            sysLog.setTakeTime((Long) threadInfo.get("takeTime"));
            sysLog.setId(UUIDUtils.get());

            sysLogService.save(sysLog);
        } catch (Exception ex) {
            log.error("保存日志错误", ex);
        }
    }

    /**
     * 获取执行时间
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object ob = proceedingJoinPoint.proceed();
        Map<String, Object> threadInfo = threadLocal.get();
        if (threadInfo !=null){
            Long takeTime = System.currentTimeMillis() - startTime;
            threadInfo.put("takeTime", takeTime);
            log.info("耗时：{} ms", takeTime);
            threadLocal.set(threadInfo);
        }
        return ob;
    }
    /**
     * 异常处理
     * @param throwable
     */
    @AfterThrowing(value = "webLog()", throwing = "throwable")
    public void doAfterThrowing(Throwable throwable) {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        // 异常信息
        log.error("{}接口调用异常，异常信息{}", request.getRequestURI(), throwable);
    }
}
